package com.zx.cloud.web.filter;

import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.regex.Pattern;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

import com.google.common.primitives.Bytes;

/**
 * Resettable HTTP servlet request stream.
 *
 * @author Petr Dvorak
 */
public class ResettableStreamHttpServletRequest extends HttpServletRequestWrapper {

	public static final byte[] EMPTY = new byte[0];
	private byte[] requestBody = EMPTY;
	private boolean bufferFilled;

	/**
	 * Constructs a request object wrapping the given request.
	 *
	 * @param request
	 *            The request to wrap
	 * @throws IllegalArgumentException
	 *             if the request is null
	 */
	public ResettableStreamHttpServletRequest(HttpServletRequest request) {
		super(request);
	}

	/**
	 * Get request body.
	 *
	 * @return Bytes with the request body contents.
	 * @throws IOException
	 *             In case stream reqding fails.
	 */
	public byte[] getRequestBody() throws IOException {
		final byte[] resultBytes;
		if (bufferFilled) {
			// 已经填入则直接返回
			resultBytes = Arrays.copyOf(requestBody, requestBody.length);
		} else {
			// 未填入则进行处理
			InputStream inputStream = super.getInputStream();
			byte[] buffer = new byte[4096];
			int bytesRead;
			while ((bytesRead = inputStream.read(buffer)) != -1) {
				requestBody = Bytes.concat(this.requestBody, Arrays.copyOfRange(buffer, 0, bytesRead));
			}
			// 置标志位
			bufferFilled = true;
			resultBytes = Arrays.copyOf(requestBody, requestBody.length);
		}
		return resultBytes;
	}

	@Override
	public ServletInputStream getInputStream() throws IOException {
		byte[] bytes = getRequestBody();
		if (bytes == null || bytes.length == 0) {
			return new ResettableServletInputStream(bytes);
		} else {
			String body = new String(bytes, this.getCharacterEncoding());
			// 很重要，把post参数重新写入请求流
			return new ResettableServletInputStream(stripXSS(body).getBytes());
		}
	}

	private String stripXSS(String value) {
		if (value != null) {
			// NOTE: It's highly recommended to use the ESAPI library and
			// uncomment the following line to
			// avoid encoded attacks.
			// value = ESAPI.encoder().canonicalize(value);
			// Avoid null characters
			value = value.replaceAll("\0", "");
			// Avoid anything between script tags
			Pattern scriptPattern = Pattern.compile("<script>(.*?)</script>", Pattern.CASE_INSENSITIVE);
			value = scriptPattern.matcher(value).replaceAll("");
			// Avoid anything between iframe tags
			Pattern iframePattern = Pattern.compile("<iframe>(.*?)</iframe>", Pattern.CASE_INSENSITIVE);
			value = iframePattern.matcher(value).replaceAll("");
			// Remove any lonesome <iframe ...> tag
			iframePattern = Pattern.compile("<iframe(.*?)>", Pattern.CASE_INSENSITIVE);
			value = iframePattern.matcher(value).replaceAll("");
			// Remove any lonesome <img ...> tag
			Pattern imgPattern = Pattern.compile("<img(.*?)>", Pattern.CASE_INSENSITIVE);
			value = imgPattern.matcher(value).replaceAll("");
			// Avoid anything in a src='...' type of expression
			scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
			value = scriptPattern.matcher(value).replaceAll("");
			scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\"(.*?)\\\"", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
			value = scriptPattern.matcher(value).replaceAll("");
			// Remove any lonesome </script> tag
			scriptPattern = Pattern.compile("</script>", Pattern.CASE_INSENSITIVE);
			value = scriptPattern.matcher(value).replaceAll("");
			// Remove any lonesome <script ...> tag
			scriptPattern = Pattern.compile("<script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
			value = scriptPattern.matcher(value).replaceAll("");
			// Avoid eval(...) expressions
			scriptPattern = Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
			value = scriptPattern.matcher(value).replaceAll("");
			// Avoid expression(...) expressions
			scriptPattern = Pattern.compile("expression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
			value = scriptPattern.matcher(value).replaceAll("");
			// Avoid javascript:... expressions
			scriptPattern = Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE);
			value = scriptPattern.matcher(value).replaceAll("");
			// Avoid vbscript:... expressions
			scriptPattern = Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE);
			value = scriptPattern.matcher(value).replaceAll("");
			// Avoid onload= expressions
			scriptPattern = Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
			value = scriptPattern.matcher(value).replaceAll("");
		}
		return value;
	}

}